#include "UP_USART.h"
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>

/* 串口缓冲区的数据长度 */
#define USART_RECEIVE_LENGTH 4096
uint8_t g_recv_buff[USART_RECEIVE_LENGTH]; // 接收缓冲区
uint16_t g_recv_length = 0;                // 接收数据长度

void USART_init(
    uint32_t USARTx,
    uint32_t baud_rate,
    uint32_t word_length,
    uint32_t stop_bits,
    uint32_t parity)
{

    UP_USART config;
    config.TR_mode = USART_TRANSMIT_MODE; // 串口发送接收模式  00禁用(0), 01接收(1), 10发送(2), 11发送接收(3)
    config.nvic_irq_pre_priority = 3;
    config.nvic_irq_sub_priority = 3;

    USART_init_with_config(USARTx, baud_rate, word_length, stop_bits, parity, config);
}

/*!
    \brief USART initialization
    \param[in]  USARTx: USARTx(x=0,1,2,5) / UARTx(x=3,4,6,7)
    \param[in]  baud_rate: 波特率 2400, 9600, 19200, 38400, 57600, 115200, 230400, 460800
    \param[in]  word_length: 数据位
      \arg       USART_WL_8BIT: 8 bits
      \arg       USART_WL_9BIT: 9 bits
    \param[in]  stop_bits: 停止位
      \arg       USART_STB_1BIT:   1 bit
      \arg       USART_STB_0_5BIT: 0.5 bit(not available for UARTx(x=3,4,6,7))
      \arg       USART_STB_2BIT:   2 bits
      \arg       USART_STB_1_5BIT: 1.5 bits(not available for UARTx(x=3,4,6,7))
    \param[in]  parity: 校验位
      \arg       USART_PM_NONE: no parity
      \arg       USART_PM_EVEN: even parity
      \arg       USART_PM_ODD:  odd parity
    \param[out] none
    \retval     none
*/
void USART_init_with_config(
    uint32_t USARTx,
    uint32_t baud_rate,
    uint32_t word_length,
    uint32_t stop_bits,
    uint32_t parity,
    UP_USART config)
{
    rcu_periph_enum tx_rcu;
    rcu_periph_enum rx_rcu;
    rcu_periph_enum rcu;
    uint32_t tx_port;
    uint32_t rx_port;
    uint32_t alt_func_num;
    uint32_t tx_pin;
    uint32_t rx_pin;

    // 声明初始 IRQn
    uint32_t irq;

    switch (USARTx)
    {

    case USART0:
        tx_rcu = RCU_GPIOA;
        rx_rcu = RCU_GPIOA;
        rcu = RCU_USART0;
        tx_port = GPIOA;
        rx_port = GPIOA;
        alt_func_num = GPIO_AF_7;
        tx_pin = GPIO_PIN_9;
        rx_pin = GPIO_PIN_10;
        irq = USART0_IRQn;
        break;
    case USART1:
        tx_rcu = RCU_GPIOA;
        rx_rcu = RCU_GPIOA;
        rcu = RCU_USART1;
        tx_port = GPIOA;
        rx_port = GPIOA;
        alt_func_num = GPIO_AF_7;
        tx_pin = GPIO_PIN_2;
        rx_pin = GPIO_PIN_3;
        irq = USART1_IRQn;
        break;
    case USART2:
        // AF7 + PB10 PB11 ----------------------------------
        tx_rcu = RCU_GPIOB;
        rx_rcu = RCU_GPIOB;
        rcu = RCU_USART2;
        tx_port = GPIOB;
        rx_port = GPIOB;
        alt_func_num = GPIO_AF_7;
        tx_pin = GPIO_PIN_10;
        rx_pin = GPIO_PIN_11;

        // 或AF7 + PC10 PC11 ----------------------------------
        // tx_rcu       = RCU_GPIOC;
        // rx_rcu       = RCU_GPIOC;
        // rcu          = RCU_USART2;
        // tx_port      = GPIOC;
        // rx_port      = GPIOC;
        // alt_func_num = GPIO_AF_7;
        // tx_pin       = GPIO_PIN_10;
        // rx_pin       = GPIO_PIN_11;
        // 或AF7 + PD8 PD9 ----------------------------------
        // tx_rcu       = RCU_GPIOD;
        // rx_rcu       = RCU_GPIOD;
        // rcu          = RCU_USART2;
        // tx_port      = GPIOD;
        // rx_port      = GPIOD;
        // alt_func_num = GPIO_AF_7;
        // tx_pin       = GPIO_PIN_8;
        // rx_pin       = GPIO_PIN_9;
        
        irq = USART2_IRQn;
        break;
    case USART5:
        // AF8 + PA11 PA12
        tx_rcu = RCU_GPIOA;
        rx_rcu = RCU_GPIOA;
        rcu = RCU_USART5;
        tx_port = GPIOA;
        rx_port = GPIOA;
        alt_func_num = GPIO_AF_8;
        tx_pin = GPIO_PIN_11;
        rx_pin = GPIO_PIN_12;

        // 或 AF8 + PC6 PC7 ----------------------------------
        // tx_rcu       = RCU_GPIOC;
        // rx_rcu       = RCU_GPIOC;
        // rcu          = RCU_USART5;
        // tx_port      = GPIOC;
        // rx_port      = GPIOC;
        // alt_func_num = GPIO_AF_8;
        // tx_pin       = GPIO_PIN_6;
        // rx_pin       = GPIO_PIN_7;

        // 或 AF8 + PG14 PG9 ---------------------------------
        // tx_rcu       = RCU_GPIOG;
        // rx_rcu       = RCU_GPIOG;
        // rcu          = RCU_USART5;
        // tx_port      = GPIOG;
        // rx_port      = GPIOG;
        // alt_func_num = GPIO_AF_8;
        // tx_pin       = GPIO_PIN_14;
        // rx_pin       = GPIO_PIN_9;
        
        irq = USART5_IRQn;
        break;
    default:
        return;
    }

    /* 开启时钟 */
    rcu_periph_clock_enable(tx_rcu); // 开启串口时钟
    rcu_periph_clock_enable(rx_rcu); // 开启端口时钟
    rcu_periph_clock_enable(rcu);    // 开启端口时钟

    /* 配置GPIO复用功能 */
    gpio_af_set(tx_port, alt_func_num, tx_pin);
    gpio_af_set(rx_port, alt_func_num, rx_pin);

    /* 配置GPIO的模式 */
    /* 配置TX为复用模式 上拉模式 */
    gpio_mode_set(tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, tx_pin);
    /* 配置RX为复用模式 上拉模式 */
    gpio_mode_set(rx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, rx_pin);

    /* 配置TX为推挽输出 50MHZ */
    gpio_output_options_set(tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, tx_pin);
    /* 配置RX为推挽输出 50MHZ */
    gpio_output_options_set(rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, rx_pin);

    /* 配置串口的参数 */
    usart_deinit(USARTx);                       // 复位串口
    usart_baudrate_set(USARTx, baud_rate);      // 设置波特率
    usart_word_length_set(USARTx, word_length); // 8位数据位
    usart_stop_bit_set(USARTx, stop_bits);      // 1位停止位
    usart_parity_config(USARTx, parity);        // 没有校验位

    /* 使能串口 */
    usart_enable(USARTx); // 使能串口

    // 根据config的TR_mode配置发送接收模式 00禁用(0), 01接收(1), 10发送(2), 11发送接收(3)
    usart_transmit_config(USARTx, (config.TR_mode & USART_TRANSMIT_MODE) ? USART_TRANSMIT_ENABLE : USART_TRANSMIT_DISABLE); // 使能串口发送
    usart_receive_config(USARTx, (config.TR_mode & USART_RECEIVE_MODE) ? USART_RECEIVE_ENABLE : USART_RECEIVE_DISABLE);     // 使能串口接收

        /* 使能NVIC中断 中断分组为2位抢占优先级，2位子优先级 需要 misc 模块支持 */
    nvic_irq_enable(irq, config.nvic_irq_pre_priority, config.nvic_irq_sub_priority); // 抢占优先级0-3，子优先级0-3

#if USART0_DMA_ENABLE
    /* 使能串口DMA接收 */
    usart_dma_receive_config(USARTx, USART_RECEIVE_DMA_ENABLE);
#else
    usart_interrupt_enable(USARTx, USART_INT_RBNE); // 读数据缓冲区非空中断和溢出错误中断
#endif
    usart_interrupt_enable(USARTx, USART_INT_IDLE); // 空闲检测中断
}

/************************************************
函数名称 ： usart_send_data
功    能 ： 串口重发送一个字节
参    数 ： ucch：要发送的字节
返 回 值 ：
*************************************************/
void usart_send_data(uint32_t USARTx, uint8_t ucch)
{
    usart_data_transmit(USARTx, (uint16_t)ucch); // 发送数据
    while (RESET == usart_flag_get(USARTx, USART_FLAG_TBE))
        ; // 等待发送数据缓冲区标志置位
}

/************************************************
函数名称 ： usart_send_String
功    能 ： 串口发送字符串
参    数 ： ucstr:要发送的字符串
返 回 值 ：
*************************************************/
void usart_send_string(uint32_t USARTx, char *ucstr)
{
    while (ucstr && *ucstr) // 地址为空或者值为空跳出
    {
        usart_send_data(USARTx, *ucstr++); // 发送单个字符
    }
}

/************************************************
函数名称 ： fputc
功    能 ： 串口重定向函数
参    数 ：
返 回 值 ：
*************************************************/
// int fputc(int ch, FILE *f)
// {
//     usart_send_data(USART0, ch);
//     // 等待发送数据缓冲区标志置位
//     return ch;
// }

int fputc(int ch, FILE *f)
{
    usart_send_data(USART0, ch);
    // 等待发送数据缓冲区标志置位
    return ch;
}

// 根据USART0_DMA_ENABLE宏的值是否为0，如果为0则声明USART0_IRQHandler
#if !USART0_DMA_ENABLE

void USART0_IRQHandler(void)
{
    if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE) == SET) // 接收缓冲区不为空
    {
        g_recv_buff[g_recv_length++] = usart_data_receive(USART0); // 把接收到的数据放到缓冲区中
    }

    if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == SET) // 检测到帧中断
    {
        usart_data_receive(USART0); // 必须要读，读出来的值不能要

        g_recv_buff[g_recv_length] = '\0'; // 数据接收完毕，数组结束标志

        // -----------------------
        on_usart_data_received(USART0, g_recv_buff, g_recv_length); // 调用回调函数
        // -----------------------

        memset(g_recv_buff, 0, g_recv_length); // 清空数组
        g_recv_length = 0;                     // 清空长度
    }
}

#else

// void on_usart_data_received(uint32_t USARTx, char *data, uint16_t length)
// {
//     // 将此方法重写，实现自己的功能
// }

#endif